【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本】

您所在的位置:网站首页 华硕 梅林 ssh connection refused 【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本】

【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本】

2024-07-17 07:22| 来源: 网络整理| 查看: 265

【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本实现法】

Sensor.Command_line Python脚本 经验分享 萌新探索系列

憋说话,先发图。 360截图16450708758970.png 登录/注册后可看大图

360截图16450708758970.png (18.78 KB, 下载次数: 0)

下载附件

2018-4-24 10:09 上传

更新 4月27日: 微信截图_20180427020412.png 登录/注册后可看大图

微信截图_20180427020412.png (2.77 KB, 下载次数: 0)

下载附件

2018-4-27 02:05 上传

计划新增用于不可描述之事的命令: _CMD_SSFS = 'nvram get ss_foreign_state' _CMD_SSCS = 'nvram get ss_china_state' 前言

最近,萌新楼主拜读了囧帅大大@Jone的神贴:梅林路由器CPU和无线芯片温度接入Home Assistant之后,有所启发。结合前期自己利用HA对路由器等网络设备进行信息监控的一些探索和经验,本楼主写了这段在HA上实现的AsusWrt固件路由器信息监控的Py脚本,并配以相应的配置文件,希望对您有所帮助。

想法

编写这个脚本的初衷纯粹是为了把玩,即用其它方法实现囧帅大大帖子中对路由器信息的监控,再就是为以后编写关于AsusWrt的自定义组件积累经验。 有兴趣的同学可以先看看囧帅大大的方法,思路很棒,利用路由器的JFFS存储并自动执行脚本获取路由器数据,然后路由器将这些信息以JSON序列主动向HA的API进行发送。囧帅大大的方法很妙,最关键是不占HA资源,HA不需要向路由器请求,只需要接收数据。 然后我这个方法,其实是土办法,在HA加载一个命令行传感器,命令行传感器执行的是一个py脚本,在脚本中通过SSH登录到路由器并通过运行命令行获得数据,数据经过正则表达式等的修正,成为有效载荷,作为HA命令行传感器的state传回到HA平台。 这种方法虽然土,但一样有效,因为只用了一个命令行传感器执行脚本,避免了SSH疯狂并发工作导致路由器Ban掉HA系统ip的窘境,经过在群晖docker下的HA上实测,工作还算稳定,虽然脚本很单薄,没有太多的异常处置,但是目前还没有报过任何错误。然后就是在本方法中路由器只需要打开SSH,JFFS之类不用打开(最关键可能就是这点,满足了我私人的需求,因为我就是纠结地银,我不想单独为监控信息这种类型的功能打开JFFS,这方法对HA来说很妙,但对路由器来说意义却不大...而且感觉这种方法的远景建设性程度比较受限,总之我个人不足以说服自己)。 那么这个方法是好方法么,当然不是啊!楼主我自己也说了,本文这种方法只能用于把玩把玩,最好的办法还是编写自定义组件!(当然,没有组件之前随便用用并没有啥问题。)

本文方法的局限性有以下几点: 这种方法中提取芯片温度的手段略显粗糙,路由器固件自身是通过ioctl函数获取温度值的,下步看看怎么改进。 本文中没有提取上传下载量,这属于应该被提取的信息,下步改进。 通过这种方法传回来的state值,最大只能255字节,HA你妹的设定,我本来是准备直接传回一整段JSON序列的,但是采集了那么一点点数据,一看JSON数据大小就要500多字节,只能砍掉字典的键、保留值并做成列表形式(砍完正好250字节左右,我地个神),字符串传回来,最后重建列表提取信息。这个问题单单在脚本中无法解决,脚本毕竟是脚本,毕竟是受限于HA的,当然破解方法也不是没有,比如在脚本中把JSON序列保存为HA本地文件,再通过另外的HA传感器把文件读出来...... 使用这种方法虽然避免了SSH连接请求短时间疯狂并发的情况,但是每一次运行命令行完毕必然导致SSH关闭,下次请求再打开,我现在设定是每30秒运行一次命令行脚本,在路由器上看到的日志就是ssh不停地打开和关闭......这个问题还是要通过编写自定义组件来解决吧。 最后就是这种方法其实和系统自带的device_tracker.asuswrt组件有很多方法是相通的重复的,未来编写自定义组件应该考虑合并掉DeviceTrack.AsusWrt的功能,有时觉得这个track组件真心不好用,看看能否改进和增加新功能,比如利用wl rssi命令,通过连接无线网络设备的MAC地址,反查设备的信号强度,以此作为tracker等功能判定的依据。不过前几天看了下device_tracker这个组件,核心文件__init__.py就比sensor组件的要复杂得多,处处涉及异步编程,光配置平台就有4种方法:async_get_scanner、get_scanner、async_setup_scanner、setup_scanner,让萌新我看得云里雾里......哎,我是一月前连python是啥都不知道,Linux一个命令也不懂的人啊,正要命。 文件 asuswrt_mirukuteii.py

建议在HA系统根目录下找个地方放这个文件,比如我新建了一个script文件夹,专门放脚本文件。

############################################# #             AsusWrt_MRKT                  # ############################################# # 本脚本为收集AsusWrt固件的路由器信息而编写    # # 实现方法:在HA中调用命令行传感器运行本脚本   # #          本脚本通过SSH连接AsusWrtRouter,  # #          取得相应信息并返回至命令行传感器。  # # 作者:Mirukuteii@hasssbian      2018-4-25  # ############################################# import re REQUIREMENTS = ['pexpect==4.0.1'] #SSH连接参数,请按路由器实际填写 _RT_IP    = 'XX.XX.XX.XX' _RT_PT    = 'XXXX' _RT_USR   = 'XXXXXXX' #SSH密码和证书两种方式2选1,空着的那个直接填''即可 _RT_SSHPWD     = 'XXXXXXXX' _RT_SSHKEYPATH = '/XX/.ssh/id_rsa' #命令行组 _CMD_NAME = 'nvram get computer_name' _CMD_WANIP = 'nvram get wan_ipaddr' _CMD_LANIP = 'nvram get lan_ipaddr' _CMD_MAC = 'nvram get lan_hwaddr' _CMD_UPTIME = 'uptime' _CMD_CPUTEMP = "cat /proc/dmu/temperature |sed -e 's/[^0-9]//g'" _CMD_24GTEMP = "wl -i eth1 phy_tempsense |awk '{print $1 / 2 + 20}'" _CMD_5GTEMP = "wl -i eth2 phy_tempsense |awk '{print $1 / 2 + 20}'" _CMD_24GTXPWR = 'wl -i eth1 txpwr_target_max' _CMD_5GTXPWR = 'wl -i eth2 txpwr_target_max' _CMD_MEM = 'top -n 1 -b |grep ^Mem' #_CMD_RSSI = wl -i eth1 rssi MAC #正则公式组 _REGEX_NAME = re.compile(     r'(?P;router_name;(.+))\s+') _REGEX_WANIP = re.compile(     r'(?P;wan_ip;(.+))\s+') _REGEX_LANIP = re.compile(     r'(?P;lan_ip;(.+))\s+') _REGEX_MAC = re.compile(     r'(?P;mac;(.+))\s+') _REGEX_UPTIME = re.compile(     r'\s' +     r'(?P;router_nowtime;(.+))\sup\s' +     r'(?P;router_uptime;(.+)),\s+load.+:\s' +     r'(?P;cpu_load;(.+))\s+') _REGEX_CPUTEMP = re.compile(     r'(?P;cpu_temp;(\d+))\s+') _REGEX_24GTEMP = re.compile(     r'(?P;wl24G_temp;(\d+))') _REGEX_5GTEMP = re.compile(     r'(?P;wl5G_temp;(\d+))') _REGEX_24GTXPWR = re.compile(     r'.+:\s+' +     r'(?P;wl24G_txpwr;(\d.+))\s') _REGEX_5GTXPWR = re.compile(     r'.+:\s+' +     r'(?P;wl5G_txpwr;(\d.+))\s') _REGEX_MEM = re.compile(     r'Mem:\s' +     r'(?P;mem_used;(\d+K))\sused,\s' +     r'(?P;mem_free;(\d+K))\sfree,\s' +     r'(?P;mem_shrd;(\d+K))\sshrd,\s' +     r'(?P;mem_buff;(\d+K))\sbuff,\s' +     r'(?P;mem_cached;(\d+K))\s.+') #定义类:_Connection class _Connection:     def __init__(self):         self._connected = False     @property     def connected(self):         """Return connection state."""         return self._connected     def connect(self):         """Mark current connection state as connected."""         self._connected = True     def disconnect(self):         """Mark current connection state as disconnected."""         self._connected = False #定义类:SshConnection,继承_Connection类 class SshConnection(_Connection):     """Maintains an SSH connection to an ASUS-WRT router."""     def __init__(self, host, port, username, password, ssh_key):         """Initialize the SSH connection properties."""         super().__init__()         self._ssh = None         self._host = host         self._port = port         self._username = username         self._password = password         self._ssh_key = ssh_key     def run_command(self, command):         """Run commands through an SSH connection.         Connect to the SSH server if not currently connected, otherwise         use the existing connection.         """         from pexpect import pxssh, exceptions         try:             if not self.connected:                 self.connect()             self._ssh.sendline(command)             self._ssh.prompt()             lines = self._ssh.before.split(b'\n')[1:-1]             return [line.decode('utf-8') for line in lines]         except exceptions.EOF as err:             #_LOGGER.error("Connection refused. %s", self._ssh.before)             self.disconnect()             return None         except pxssh.ExceptionPxssh as err:             #_LOGGER.error("Unexpected SSH error: %s", err)             self.disconnect()             return None         except AssertionError as err:             #_LOGGER.error("Connection to router unavailable: %s", err)             self.disconnect()             return None     def connect(self):         """Connect to the ASUS-WRT SSH server."""         from pexpect import pxssh         self._ssh = pxssh.pxssh()         if self._ssh_key:             self._ssh.login(self._host, self._username, quiet=False,                             ssh_key=self._ssh_key, port=self._port)         else:             self._ssh.login(self._host, self._username, quiet=False,                             password=self._password, port=self._port)         super().connect()     def disconnect(self):   \             # pylint: disable=broad-except         """Disconnect the current SSH connection."""         try:             self._ssh.logout()         except Exception:             pass         finally:             self._ssh = None         super().disconnect() #定义函数_parse_lines(),从命令行返回值中按正则公式提取信息,返回为一个列表 def _parse_lines(lines, regex):     """Parse the lines using the given regular expression.     If a line can't be parsed it is logged and skipped in the output.     """     results = []     for line in lines:         match = regex.search(line)         if not match:             #_LOGGER.debug("Could not parse row: %s", line)             continue         results.append(match.groupdict())     return results #定义函数get_data_by(),执行命令行语句cmd_line; #并将返回值交给函数_parse_lines(); #按照正则公式regex_rule提取信息,作为本函数返回值. def get_data_by(connection, cmd_line, regex_rule):     lines = connection.run_command(cmd_line)     if not lines:         return {}     result = _parse_lines(lines, regex_rule)     data = {}     for element in result:         data.update(element)     return data #定义函数get_asuswrt_data,完成打开SSH连接并提取并打印数据的整个过程 def get_asuswrt_data(connection):     asuswrt_data = {}     asuswrt_data.update(get_data_by(connection, _CMD_WANIP, _REGEX_WANIP))     asuswrt_data.update(get_data_by(connection, _CMD_LANIP, _REGEX_LANIP))     asuswrt_data.update(get_data_by(connection, _CMD_MAC, _REGEX_MAC))     asuswrt_data.update(get_data_by(connection, _CMD_UPTIME, _REGEX_UPTIME))     asuswrt_data.update(get_data_by(connection, _CMD_CPUTEMP, _REGEX_CPUTEMP))     asuswrt_data.update(get_data_by(connection, _CMD_24GTEMP, _REGEX_24GTEMP))     asuswrt_data.update(get_data_by(connection, _CMD_5GTEMP, _REGEX_5GTEMP))     asuswrt_data.update(get_data_by(connection, _CMD_24GTXPWR, _REGEX_24GTXPWR))     asuswrt_data.update(get_data_by(connection, _CMD_5GTXPWR, _REGEX_5GTXPWR))     asuswrt_data.update(get_data_by(connection, _CMD_MEM, _REGEX_MEM))     dict = get_data_by(connection, _CMD_NAME, _REGEX_NAME)     json_data = {'state': dict['router_name'], 'attributes': asuswrt_data}     #本来这里可以str(json_data)返回了,怎奈HA只接受255B以内的数据     list_data = [dict['router_name']]     for key in asuswrt_data:         list_data.append(asuswrt_data[key])     str_data = ','.join(list_data)     print(str_data) if __name__ == '__main__':     asuswrt_connection = SshConnection(_RT_IP, _RT_PT, _RT_USR, _RT_SSHPWD, _RT_SSHKEYPATH)     get_asuswrt_data(asuswrt_connection) asuswrt_mirukuteii.yaml

建议放在packages文件夹中工作,也可修改添加到configuration.yaml中。

#AsusWrt路由器信息监控 sensor:   - platform: command_line     name: router     #下面的路径按py文件的存放位置进行修改哦     command: "python3 /config/script/asuswrt_mirukuteii.py"     scan_interval: 30   - platform: template     sensors:       router_name:         icon_template: mdi:router-wireless         friendly_name: "路由器名称"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[0] }}       router_wanip:         icon_template: mdi:ethernet         friendly_name: "外网IP地址"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[1] }}       router_lanip:         icon_template: mdi:ethernet         friendly_name: "内网IP地址"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[2] }}       router_mac:         icon_template: mdi:ethernet         friendly_name: "路由器MAC地址"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[3] }}       router_nowtime:         icon_template: mdi:clock         friendly_name: "路由器当前时间"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[4] }}       router_uptime:         icon_template: mdi:av-timer         friendly_name: "路由器运行时间"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[5] }} {{ list[6] }}       router_load_1min:         icon_template: mdi:select-inverse         friendly_name: "CPU平均负载:1分钟"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[7] }}       router_load_5min:         icon_template: mdi:select-inverse         friendly_name: "CPU平均负载:5分钟"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[8] }}       router_load_15min:         icon_template: mdi:select-inverse         friendly_name: "CPU平均负载:15分钟"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[9] }}       router_cputemp:         icon_template: mdi:thermometer         friendly_name: "CPU温度"         unit_of_measurement: '℃'         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[10] }}       router_24gtemp:         icon_template: mdi:thermometer         friendly_name: "2.4G温度"         unit_of_measurement: '℃'         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[11] }}       router_5gtemp:         icon_template: mdi:thermometer         friendly_name: "5G温度"         unit_of_measurement: '℃'         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[12] }}       router_24gtxpwr:         icon_template: mdi:wifi         friendly_name: "2.4G天线发射功率"         unit_of_measurement: 'dBm'         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[13] }}       router_5gtxpwr:         icon_template: mdi:wifi         friendly_name: "5G天线发射功率"         unit_of_measurement: 'dBm'         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {{ list[14] }}       router_mem_used:         icon_template: mdi:memory         friendly_name: "已用内存"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {%- set mem = (list[15] |replace('K','') |int) -%}           {%- if mem ;= 1024 -%}             {%- set mem = mem / 1024 -%}             {{ mem | round(1) }} MiB           {%- else -%}             {{ mem }} KiB           {%- endif -%}       router_mem_free:         icon_template: mdi:memory         friendly_name: "可用内存"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {%- set mem = (list[16] |replace('K','') |int) -%}           {%- if mem ;= 1024 -%}             {%- set mem = mem / 1024 -%}             {{ mem | round(1) }} MiB           {%- else -%}             {{ mem }} KiB           {%- endif -%}       router_mem_shrd:         icon_template: mdi:memory         friendly_name: "共享内存"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {%- set mem = (list[17] |replace('K','') |int) -%}           {%- if mem ;= 1024 -%}             {%- set mem = mem / 1024 -%}             {{ mem | round(1) }} MiB           {%- else -%}             {{ mem }} KiB           {%- endif -%}       router_mem_buff:         icon_template: mdi:memory         friendly_name: "磁盘缓存"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {%- set mem = (list[18] |replace('K','') |int) -%}           {%- if mem ;= 1024 -%}             {%- set mem = mem / 1024 -%}             {{ mem | round(1) }} MiB           {%- else -%}             {{ mem }} KiB           {%- endif -%}       router_mem_cached:         icon_template: mdi:memory         friendly_name: "文件缓存"         value_template: ;-           {%- set list = states.sensor.router.state.split(',') -%}           {%- set mem = (list[19] |replace('K','') |int) -%}           {%- if mem ;=1024 -%}             {%- set mem = mem / 1024 -%}             {{ mem | round(1) }} MiB           {%- else -%}             {{ mem }} KiB           {%- endif -%} group:   routermon:     name: 'ROUTER监控'     view: no     entities:       - sensor.router_name       - sensor.router_wanip       - sensor.router_lanip       - sensor.router_mac       - sensor.router_nowtime       - sensor.router_uptime       - sensor.router_load_1min       - sensor.router_load_5min       - sensor.router_load_15min       - sensor.router_cputemp       - sensor.router_24gtemp       - sensor.router_5gtemp       - sensor.router_24gtxpwr       - sensor.router_5gtxpwr       - sensor.router_mem_used       - sensor.router_mem_free       - sensor.router_mem_shrd       - sensor.router_mem_buff       - sensor.router_mem_cached Groups.yaml

在这个文件中某个你想要展示路由器监控情况项目的group的entities项中添加如下代码以实现图片中的状态卡片。

- group.routermon


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3